home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / ms-0.07 / xms / Ms.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-27  |  28.3 KB  |  978 lines

  1. /* Ms.c - MandelSpawn popup widget */
  2.  
  3. /*  
  4.     This file is part of MandelSpawn, a network Mandelbrot program.
  5.  
  6.     Copyright (C) 1990-1993 Andreas Gustafsson
  7.  
  8.     MandelSpawn is free software; you can redistribute it and/or modify
  9.     it under the terms of the GNU General Public License, version 1,
  10.     as published by the Free Software Foundation.
  11.  
  12.     MandelSpawn is distributed in the hope that it will be useful,
  13.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.     GNU General Public License for more details.
  16.  
  17.     You should have received a copy of the GNU General Public License,
  18.     version 1, along with this program; if not, write to the Free 
  19.     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. */
  21.  
  22. /* This widget creates a window showing a part of the Mandelbrot set */
  23.  
  24. #include <stdio.h>
  25. #include <math.h>
  26.  
  27. #include <X11/IntrinsicP.h>
  28. #include <X11/Xos.h>
  29. #include <X11/StringDefs.h>
  30.  
  31. #include "backward.h" /* X11R2 backward compatibility stuff */
  32.  
  33. #include "MsP.h" /* includes Ms.h which includes MsJob.h */
  34. #include "Mama.h"
  35.  
  36. extern XtAppContext thisApp;
  37. extern Display *myDisplay;
  38. extern Screen *myScreen;
  39.  
  40. /* Misc. functions */
  41. static void
  42.     Initialize(), Resize(), Realize(), Destroy(), DoExpose(),
  43.     EraseBox(), Die();
  44.  
  45. /* Action functions */
  46. static void
  47.     BeginBoxAction(), StretchBoxAction(), EndBoxAction(),
  48.     ZoomAction(), CloseAction(), QuitAction(), ApplStatsAction(), 
  49.     WindowStatsAction();
  50.  
  51. #ifdef MENU
  52. void MsCreateMenu();
  53. #endif
  54. #ifdef LABEL
  55. void MsCreateUnderflowLabel();
  56. #endif
  57.  
  58. static Boolean SetValues();
  59.  
  60.  
  61. /*
  62.   Supported visual type / iteration count length combinations
  63.   are obtained by ORing togeter one ITER_x and one DISP_x value 
  64. */
  65.  
  66. #define ITER_BYTE        0  /* 8-bit iteration counts */
  67. #define ITER_WORD        1  /* 16-bit iteration counts */
  68.  
  69. #define DISP_unknown        0
  70. #define DISP_1plane32msb     2  /* like a Sun monochrome display */
  71. #define DISP_1plane32lsb     4  /* like a DECstation monochrome display */
  72. #define DISP_8plane        6  /* typical 8-plane framebuffer */
  73. #define DISP_32plane        8  /* typical 24/32-plane framebuffer */
  74. #define DISP_generic           10  /* none of the above */
  75.  
  76. /* Magic for finding out the machine's byte order */
  77.  
  78. static int endian_magic = 1;
  79. #define CPU_LITTLE_ENDIAN() (*((char *) &endian_magic) == 1)
  80.  
  81. /* Defaults */
  82.  
  83. static Dimension default_width = 400;    /* window width in pixels */
  84. static Dimension default_height = 250;    /* window height in pixels */
  85. static int default_iteration_limit = 0;    /* 0 means undefined */
  86. static double default_center_x = (-0.5); /* x coordinate of window center */
  87. static double default_center_y = 0.0;    /* y coordinate of window center */
  88. static double default_range = 4.0;     /* window range in x direction */
  89. static Bool default_center_box = True;    /* do we center the rubberband box? */
  90. static Bool default_julia = False;    /* do we show the Julia set? */
  91. static double default_c_x = 0.0;     /* c parameter for Julia, real */
  92. static double default_c_y = 0.0;    /* c parameter for Julia, im */
  93. static double default_julia_range = 4.0; /* window range for Julia */
  94. static double default_julia_center_x = 0.0; /* window center x for Julia */
  95. static double default_julia_center_y = 0.0; /* window center y for Julia */
  96. static char default_cursor[] = "top_left_arrow"; 
  97. static unsigned default_chunk_width = 32;
  98. static unsigned default_chunk_height = 32;
  99. static Bool default_sony_bug_workaround = False;
  100. static Bool default_crosshair_size = 3;
  101. static Bool default_show_interior = False;
  102.  
  103.  
  104. extern char msDefaultTranslations[];
  105.  
  106. #ifndef MENU
  107. /*
  108.   Some of these bindings may be less than obvious, but you shouldn't
  109.   be using them anyway now that there is a popup menu.   There is
  110.   a separate set of bindings in menu.c that is used when compiled
  111.   with menu support.
  112. */
  113. char msDefaultTranslations[] = 
  114.     "<Btn1Down>:    BeginBox()        \n\
  115.      <Btn1Motion>:    StretchBox()        \n\
  116.      <Btn1Up>:        EndBox()        \n\
  117.      Shift<Btn2Down>:    Zoom(nopop,nojulia,in)    \n\
  118.      <Btn2Down>:    Zoom(popup,nojulia,in)    \n\
  119.      Shift<Btn3Down>:    Quit()            \n\
  120.      <Btn3Down>:    Close()            \n\
  121.      Shift<Key>z:    Zoom(nopop,nojulia,in)    \n\
  122.      <Key>z:        Zoom(popup,nojulia,in)    \n\
  123.      Shift<Key>o:    Zoom(nopop,nojulia,out)    \n\
  124.      <Key>o:        Zoom(popup,nojulia,out)    \n\
  125.      Shift<Key>j:    Zoom(nopop,julia,in)    \n\
  126.      <Key>j:        Zoom(popup,julia,in)    \n\
  127.      <Key>s:        ApplStats()        \n\
  128.      <Key>w:        WindowStats()        \n\
  129.      <Key>c:        Close()            \n\
  130.      <Key>q:        Quit()            \n\
  131.   ";
  132. #endif
  133.  
  134. static XtActionsRec actionsList[] =
  135. {
  136.     { "BeginBox",    BeginBoxAction        },
  137.     { "EndBox",        EndBoxAction        },
  138.     { "StretchBox",    StretchBoxAction    },
  139.     { "Zoom",        ZoomAction        },
  140.     { "WindowStats",    WindowStatsAction    },
  141.     { "ApplStats",    ApplStatsAction        },
  142.     { "Close",        CloseAction        },
  143.     { "Quit",        QuitAction        }
  144. };
  145.  
  146. static XtResource resources[] = 
  147. {   /* Core resources */
  148.     { XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
  149.     XtOffset(Widget, core.width), XtRDimension,
  150.     (caddr_t) &default_width },
  151.     { XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
  152.     XtOffset(Widget, core.height), XtRDimension,
  153.     (caddr_t) &default_height },
  154.     /* Noncore resources */
  155.     { XtNiteration_limit, XtCValue, XtRInt, sizeof(int),
  156.     XtOffset(MsWidget, ms.xi.job.iteration_limit), XtRInt,
  157.     (caddr_t) &default_iteration_limit },
  158.     { XtNCenterX, XtCValue, XtRDouble, sizeof(double),
  159.     XtOffset(MsWidget, ms.xi.center_x), XtRDouble,
  160.     (caddr_t) &default_center_x },
  161.     { XtNCenterY, XtCValue, XtRDouble, sizeof(double),
  162.     XtOffset(MsWidget, ms.xi.center_y), XtRDouble,
  163.     (caddr_t) &default_center_y },
  164.     { XtNRange, XtCValue, XtRDouble, sizeof(double),
  165.     XtOffset(MsWidget, ms.xi.xrange), XtRDouble,
  166.     (caddr_t) &default_range },
  167.     { XtNCenterBox, XtCValue, XtRBool, sizeof(Bool),
  168.     XtOffset(MsWidget, ms.center_box), XtRBool,
  169.     (caddr_t) &default_center_box },
  170.     { XtNCursor, XtCValue, XtRCursor, sizeof(Cursor),
  171.     XtOffset(MsWidget, ms.my_cursor), XtRString,
  172.     (caddr_t) default_cursor },
  173.     { XtNMama, XtCValue, XtRPointer, sizeof(MamaWidget),
  174.     XtOffset(MsWidget, ms.mama), XtRPointer,
  175.     (caddr_t) 0 } ,
  176.     { XtNJulia, XtCValue, XtRBool, sizeof(Bool),
  177.     XtOffset(MsWidget, ms.xi.julia), XtRBool,
  178.     (caddr_t) &default_julia },
  179.     { XtNCX, XtCValue, XtRDouble, sizeof(double),
  180.     XtOffset(MsWidget, ms.xi.c_x), XtRDouble,
  181.     (caddr_t) &default_c_x },
  182.     { XtNCY, XtCValue, XtRDouble, sizeof(double),
  183.     XtOffset(MsWidget, ms.xi.c_y), XtRDouble,
  184.     (caddr_t) &default_c_y },
  185.     { XtNChunkWidth, XtCValue, XtRInt, sizeof(unsigned int),
  186.     XtOffset(MsWidget, ms.xi.chunk_width), XtRInt,
  187.     (caddr_t) &default_chunk_width },
  188.     { XtNChunkHeight, XtCValue, XtRInt, sizeof(unsigned int),
  189.     XtOffset(MsWidget, ms.xi.chunk_height), XtRInt,
  190.     (caddr_t) &default_chunk_height },
  191.     { XtNSony, XtCValue, XtRBool, sizeof(Bool),
  192.         XtOffset(MsWidget, ms.sony_bug_workaround), XtRBool,
  193.         (caddr_t) &default_sony_bug_workaround },
  194.     { XtNCrosshairSize, XtCValue, XtRInt, sizeof(int),
  195.         XtOffset(MsWidget, ms.crosshair_size), XtRInt,
  196.         (caddr_t) &default_crosshair_size },
  197.     { XtNInterior, XtCValue, XtRBool, sizeof(Bool),
  198.         XtOffset(MsWidget, ms.xi.show_interior), XtRBool,
  199.         (caddr_t) &default_show_interior }
  200.  
  201. };
  202.  
  203. MsClassRec msClassRec = 
  204. {   /* core fields */
  205.     { 
  206.     /* superclass        */    (WidgetClass) &compositeClassRec,
  207.     /* class_name        */    "MandelSpawn",
  208.     /* widget_size        */    sizeof(MsRec),
  209.     /* class_initialize        */      NULL,
  210.     /* class_part_initialize    */    NULL,
  211.     /* class_inited        */    FALSE,
  212.     /* initialize        */    Initialize,
  213.     /* initialize_hook        */    NULL,
  214.     /* realize            */    Realize,
  215.     /* actions            */    actionsList,
  216.     /* num_actions        */    XtNumber(actionsList),
  217.     /* resources        */    resources,
  218.     /* resource_count        */    XtNumber(resources),
  219.     /* xrm_class        */    NULL,
  220.     /* compress_motion        */    TRUE,
  221.     /* compress_exposure    */    FALSE,
  222.     /* compress_enterleave    */    TRUE,
  223.     /* visible_interest        */    FALSE,
  224.     /* destroy            */    Destroy,
  225.     /* resize            */    Resize,
  226.     /* expose            */    DoExpose,
  227.     /* set_values        */    SetValues,
  228.     /* set_values_hook        */    NULL,
  229.     /* set_values_almost    */    XtInheritSetValuesAlmost,
  230.     /* get_values_hook        */    NULL,
  231.     /* accept_focus        */    NULL,
  232.     /* version            */    XtVersion,
  233.     /* callback_private        */    NULL,
  234.     /* tm_table            */    msDefaultTranslations,
  235.     /* query_geometry        */    NULL
  236.     },
  237.     /* CompositeClassPart fields */
  238.     { XtInheritGeometryManager,
  239.       XtInheritChangeManaged,
  240.       XtInheritInsertChild,
  241.       XtInheritDeleteChild
  242.     },
  243.     /* msClassPart fields */
  244.     {
  245.     /* dummy            */    0
  246.     }
  247. };
  248.  
  249. WidgetClass msWidgetClass = (WidgetClass) &msClassRec;
  250.  
  251.  
  252. /* Initialize the widget */
  253.  
  254. static void
  255. Initialize(request, new)
  256.      MsWidget   request;
  257.      MsWidget   new;
  258. { XGCValues gcv;
  259.   int depth = MamaVisualInfo(new->ms.mama)->depth;
  260.   /* set up a GC for the rubberband box */
  261.   gcv.function=GXxor;
  262.   
  263.   /*
  264.     "gcv.foreground=~0l" messes up the black-and-white mode of the 
  265.      X11R2 server for the Sony NWS-1510 4-plane display quite badly; 
  266.      I'd call that a server bug.  The code below works but assumes 
  267.      2's complement. 
  268.   */
  269.   if(depth==32)
  270.     gcv.foreground=0xffffffff; /* 1<<32 is undefined, says K&R */
  271.   else
  272.     gcv.foreground = (1 << depth) - 1;
  273.   new->ms.box_gc = XtGetGC((Widget) new, GCFunction|GCForeground, &gcv);
  274.  
  275.   new->ms.blit_gc = XtGetGC((Widget) new, 0L, &gcv);
  276.   
  277.   new->ms.box_exists = False;
  278.   new->ms.rectbuffer=(XImage *) NULL; /* no rectangle buffer so far */
  279. #ifdef LABEL
  280.   new->ms.underflow=0;
  281.   new->ms.underflow_label=NULL;
  282. #endif
  283.   ms_init(&new->ms.xi, (char *) new, MamaWorkforce(new->ms.mama));
  284.  
  285.   XtAddCallback((Widget) new, XtNdestroyCallback,
  286.         (XtCallbackProc) Die, (caddr_t) 0);
  287. }
  288.  
  289.  
  290. /*
  291.   Initialize, or re-initialize widget fields that may need to be 
  292.   changed when the widget is resized, zoomed or otherwise modified 
  293. */
  294.  
  295. static void MsPrecalculate(w) MsWidget w;
  296. { int underflow;
  297.   XVisualInfo *vi = MamaVisualInfo(w->ms.mama);
  298.   w->ms.xi.height = w->core.height;
  299.   w->ms.xi.width = w->core.width;
  300.   
  301.   ms_calculate_job_parameters(&w->ms.xi, &w->ms.xi.job);
  302.  
  303. #ifdef LABEL
  304.   underflow=(w->ms.xi.job.delta.re == 0 || w->ms.xi.job.delta.im == 0);
  305.  
  306.   if(underflow && !w->ms.underflow)
  307.   { MsCreateUnderflowLabel(w);
  308.   }
  309.   if(!underflow && w->ms.underflow)
  310.   { XtDestroyWidget(w->ms.underflow_label);
  311.   }
  312.   w->ms.underflow = underflow;
  313. #endif
  314.  
  315.   /* if it had a rectangle buffer already, destroy it */
  316.   if(w->ms.rectbuffer)
  317.   {
  318.     /*
  319.       do *not* free w->ms.rectbuffer->data separately;
  320.       XDestroyImage frees the pixel data even though
  321.       XCreateImage didn't allocate it.  I hate X. 
  322.     */
  323.     XDestroyImage(w->ms.rectbuffer);
  324.   }
  325.  
  326.   /* create the rectangle buffer */
  327.   w->ms.rectbuffer=
  328.     XCreateImage(XtDisplay(w), vi->visual, vi->depth,
  329.          ZPixmap, 0, (char *) NULL, 
  330.          w->ms.xi.chunk_width, w->ms.xi.chunk_height, 32,
  331.          0 /* zero means let XCreateImage determine bytes/line */
  332.         );
  333.   w->ms.rectbuffer_size=(unsigned) w->ms.rectbuffer->bytes_per_line * 
  334.     w->ms.xi.chunk_height;
  335.   w->ms.rectbuffer->data=(char *) XtMalloc(w->ms.rectbuffer_size);
  336.  
  337.   if(w->ms.xi.job.iteration_limit == 0) /* not set yet? */
  338.   { /* iterate as far as possible by default */
  339.     w->ms.xi.job.iteration_limit = MaxIterations(w->ms.mama);
  340.   }
  341.   else
  342.     if(w->ms.xi.job.iteration_limit > MaxIterations(w->ms.mama))
  343.     { w->ms.xi.job.iteration_limit=MaxIterations(w->ms.mama);
  344.       XtAppWarning(thisApp,
  345.            "Iteration limit truncated");
  346.     }
  347.  
  348.   w->ms.xi.bytes_per_count=(w->ms.xi.job.iteration_limit > 256 ? 2 : 1);
  349.  
  350.   /*
  351.     determine the kind of draw routine to use depending on
  352.     display characteristics and iteration count size 
  353.   */
  354.  
  355.   if(w->ms.rectbuffer->bits_per_pixel == 8)
  356.     w->ms.type=DISP_8plane;
  357.   else if(w->ms.rectbuffer->bits_per_pixel == 1 &&
  358.         w->ms.rectbuffer->bitmap_unit==32 &&
  359.         w->ms.rectbuffer->bitmap_bit_order == MSBFirst)
  360.       w->ms.type=DISP_1plane32msb;
  361.   else if(w->ms.rectbuffer->bits_per_pixel == 1 &&
  362.             w->ms.rectbuffer->bitmap_unit==32 &&
  363.             w->ms.rectbuffer->bitmap_bit_order == LSBFirst)
  364.       w->ms.type=DISP_1plane32lsb;
  365.   else
  366.       if(w->ms.rectbuffer->bits_per_pixel == 32 &&
  367.      w->ms.rectbuffer->bitmap_unit == 32)
  368.       w->ms.type=DISP_32plane;
  369.   else 
  370.   { w->ms.type=DISP_generic;
  371.     XtAppWarning(thisApp,
  372.          "No special case support for this display type;\n\
  373. drawing a pixel at a time, this will be very slow.");
  374.   }
  375.  
  376.   /* This should cause Xlib to do byte swapping when necessary */
  377.   if(w->ms.type==DISP_1plane32lsb || w->ms.type==DISP_1plane32msb)
  378.     w->ms.rectbuffer->byte_order = CPU_LITTLE_ENDIAN() ? LSBFirst : MSBFirst;
  379.     
  380.   w->ms.type |= (w->ms.xi.bytes_per_count==2) ? ITER_WORD : ITER_BYTE;
  381.  
  382.   /* We now have a new configuration; give it a unique number */
  383.   w->ms.xi.configuration++;
  384. }
  385.  
  386.  
  387. /*
  388.   Calculate the coordinates for the rubberband box given the initial
  389.   and current mouse position (box_origin, box_corner). Use the box_origin
  390.   as either the center or the oppsite corner depending on the center_box
  391.   flag 
  392. */
  393.  
  394. struct box UnflipBox(w)
  395.      MsWidget w;
  396. { struct box r;
  397.   if(w->ms.center_box)
  398.   { int halfwidth=ABS(w->ms.box_corner.x - w->ms.box_origin.x);
  399.     int halfheight=ABS(w->ms.box_corner.y - w->ms.box_origin.y);
  400.     r.x0=w->ms.box_origin.x-halfwidth;
  401.     r.x1=w->ms.box_origin.x+halfwidth;
  402.     r.y0=w->ms.box_origin.y-halfheight;
  403.     r.y1=w->ms.box_origin.y+halfheight;
  404.   }
  405.   else
  406.   { r.x0=MIN(w->ms.box_origin.x, w->ms.box_corner.x);
  407.     r.x1=MAX(w->ms.box_origin.x, w->ms.box_corner.x);
  408.     r.y0=MIN(w->ms.box_origin.y, w->ms.box_corner.y);
  409.     r.y1=MAX(w->ms.box_origin.y, w->ms.box_corner.y);
  410.   }
  411.   return(r);
  412. }
  413.  
  414.  
  415. /*
  416.   The documentation for XtSetArg says that arguments are passed by
  417.   value if they fit in an XtArgVal, and by address otherwise.  This
  418.   has the unfortunate effect that a "double" value may be passed
  419.   differently on different machines: in particular, on a DEC Alpha
  420.   running OSF/1, a "double" does fit in an XtArgVal, and on most other
  421.   machines, it doesn't.
  422.  
  423.   This function decides which is appropriate.  Note that the "if"
  424.   condition can be determined at compile time, which may cause
  425.   spurious warnings from some compilers.  
  426. */
  427.  
  428. XtArgVal dbl_arg(dp)
  429.   double *dp;
  430. { if(sizeof(XtArgVal) >= sizeof(double))
  431.   { union { double d; XtArgVal x; } u;
  432.     u.d = *dp;
  433.     return u.x;
  434.   }
  435.   else
  436.     return (XtArgVal) dp;
  437. }
  438.  
  439.  
  440. /* 
  441.   Zoom into the area selected using the box.  If "pop", pop up a new
  442.   window.  If "julia", show the Julia set corresponding to the point
  443.   at the center of the box.  If "outwards", zoom out instead of in.
  444. */
  445.  
  446. void ZoomIn(w, pop, julia, outwards)
  447.        MsWidget w;
  448.        int pop;
  449.        int julia;
  450.        int outwards;
  451. { struct box b;
  452.   double scale;
  453.   double new_xrange, new_center_x, new_center_y;
  454.   Arg arglist[16];
  455.   int num_args;
  456.   Arg shell_arglist[16];
  457.   int num_shell_args;
  458.   int boxwidth;
  459.   double nx, ny;
  460.  
  461.   if(!w->ms.box_exists)
  462.     return;
  463.  
  464.   b = UnflipBox(w);  
  465.  
  466.   boxwidth = b.x1 - b.x0;
  467.  
  468.   /* caculate location of box center normalized so that 0 = window center, */
  469.   /* +-0.5 = window edge */
  470.   nx = ((((double) b.x0+b.x1)/2)-(w->core.width/2)) / (double)(w->core.width);
  471.   ny = ((((double) b.y0+b.y1)/2)-(w->core.height/2)) / (double)(w->core.height);
  472.       
  473.   if(outwards) /* zoom out */
  474.   { if(boxwidth==0)
  475.     { /* avoid division by zero */
  476.       new_xrange = default_range;
  477.       new_center_x = w->ms.xi.center_x;
  478.       new_center_y = w->ms.xi.center_y;
  479.     }
  480.     else
  481.     { scale = (double) w->core.width / (double) boxwidth;
  482.       new_xrange = w->ms.xi.xrange * scale;
  483.       new_center_x = w->ms.xi.center_x - nx * scale * w->ms.xi.xrange;
  484.       new_center_y = w->ms.xi.center_y - ny * scale * w->ms.xi.yrange;
  485.     }
  486.   }
  487.   else /* zoom in */
  488.   { scale = (double) boxwidth / (double) (w->core.width);
  489.     new_xrange = w->ms.xi.xrange * scale;
  490.     new_center_x = w->ms.xi.center_x + nx * w->ms.xi.xrange;
  491.     new_center_y = w->ms.xi.center_y + ny * w->ms.xi.yrange;
  492.   }
  493.   
  494.   /* build arguments for the changed resources in the zoomed widget */
  495.   num_args=0;
  496.  
  497.   /* Transition from Mandelbrot to Julia set */
  498.   if(julia && !(w->ms.xi.julia))
  499.   { XtSetArg(arglist[num_args], XtNJulia, True); num_args++;
  500.     /* Set the C parameter (chooses a Julia set out of infinitely many) */
  501.     XtSetArg(arglist[num_args], XtNCX, dbl_arg(&new_center_x)); num_args++;
  502.     XtSetArg(arglist[num_args], XtNCY, dbl_arg(&new_center_y)); num_args++;
  503.     /* This is an initial Julia picture, so show the whole Julia set */
  504.     XtSetArg(arglist[num_args], XtNRange, dbl_arg(&default_julia_range));
  505.       num_args++;
  506.     XtSetArg(arglist[num_args], XtNCenterX,
  507.          dbl_arg(&default_julia_center_x)); num_args++;
  508.     XtSetArg(arglist[num_args], XtNCenterY,
  509.          dbl_arg(&default_julia_center_y)); num_args++;
  510.   }
  511.   else /* No change in M/J mode */
  512.   { XtSetArg(arglist[num_args], XtNJulia, w->ms.xi.julia); num_args++;
  513.     XtSetArg(arglist[num_args], XtNCenterX, dbl_arg(&new_center_x));
  514.       num_args++;
  515.     XtSetArg(arglist[num_args], XtNCenterY, dbl_arg(&new_center_y)); 
  516.       num_args++;
  517.     XtSetArg(arglist[num_args], XtNRange, dbl_arg(&new_xrange)); num_args++;
  518.     /* these two parameters are redundant in the Mandelbrot mode */
  519.     XtSetArg(arglist[num_args], XtNCX, dbl_arg(&w->ms.xi.c_x)); num_args++;
  520.     XtSetArg(arglist[num_args], XtNCY, dbl_arg(&w->ms.xi.c_y)); num_args++;
  521.   }
  522.  
  523.   XtSetArg(arglist[num_args], XtNMama, w->ms.mama); num_args++;
  524.  
  525.   if(pop)
  526.   { num_shell_args = 0;
  527.     /* make the new window as big as this one initially */
  528.     XtSetArg(shell_arglist[num_shell_args], XtNheight, w->core.height);
  529.     num_shell_args++;
  530.     XtSetArg(shell_arglist[num_shell_args], XtNwidth, w->core.width);
  531.     num_shell_args++;
  532.     /* create a new pop-up window for the zoomed area */
  533.     PopupAnother(w->ms.mama, shell_arglist, num_shell_args, arglist, num_args);
  534.   }
  535.   else 
  536.   {
  537.     /* zoom using the old window: just set the changed resources */
  538.     EraseBox(w);
  539.     XtSetValues((Widget) w, arglist, num_args);
  540.   }
  541. }
  542.  
  543.  
  544. /* Action routine interface to ZoomIn */
  545.  
  546. static void ZoomAction(w, event, params, nparams)
  547.      MsWidget w;
  548.      XEvent *event;
  549.      String *params;
  550.      Cardinal *nparams;
  551.   if(*nparams != 3)
  552.   { XtAppWarning(thisApp, "ZoomAction: wrong number of arguments");
  553.     return;
  554.   }
  555.   ZoomIn(w, params[0][0] == 'p', params[1][0] == 'j', params[2][0] == 'o');
  556. }
  557.  
  558.  
  559. /* Handle widget exposure */
  560. /* Can't call this "Expose" because that is #defined to 12 in X.h! */
  561.  
  562. /*ARGSUSED*/
  563. static void DoExpose (w, e, r)
  564.      MsWidget    w;
  565.      XEvent *e;
  566.      Region r;
  567. {
  568. #ifdef LABEL
  569.   /* It's no use redrawing if underflow has occured */
  570.   if(w->ms.underflow)
  571.     return;
  572. #endif
  573.  
  574.   ms_dispatch_rect(&w->ms.xi, (char *) w,
  575.            e->xexpose.x, e->xexpose.y,
  576.            e->xexpose.width, e->xexpose.height
  577.           );
  578. }
  579.  
  580.  
  581. /* Draw (or undraw) the rubberband box */
  582.  
  583. static void InvertBox(w)
  584.      MsWidget w;
  585. { struct box b;
  586.   int armx, army;
  587.   armx = army = w->ms.crosshair_size;
  588.   b = UnflipBox(w);
  589.   XDrawRectangle(XtDisplay(w), XtWindow(w), w->ms.box_gc,
  590.          b.x0, b.y0, b.x1-b.x0, b.y1-b.y0);
  591.   if(armx || army)
  592.   { /* draw a crosshair at the center of the selected area */
  593.     int xc = (b.x0+b.x1)/2;
  594.     int yc = (b.y0+b.y1)/2;
  595.     XDrawLine(XtDisplay(w), XtWindow(w), w->ms.box_gc,
  596.           xc-armx, yc, xc+armx, yc);
  597.     XDrawLine(XtDisplay(w), XtWindow(w), w->ms.box_gc,
  598.           xc, yc-army, xc, yc+army);
  599.   }
  600. }
  601.  
  602.  
  603. /* Erase the rubberband box if it exists */
  604.  
  605. static void DrawBox(w)
  606.      MsWidget w;
  607. { InvertBox(w);
  608.   w->ms.box_exists = !(w->ms.box_exists);
  609. }
  610.  
  611. static void EraseBox(w)
  612.      MsWidget w;
  613. { if(w->ms.box_exists)
  614.     DrawBox(w);
  615. }
  616.  
  617. /* Create the rubberband box */
  618.  
  619. static void BeginBoxAction(w, event, params, nparams)
  620.      MsWidget w;
  621.      XEvent *event;
  622.      String *params;
  623.      Cardinal *nparams;
  624. { EraseBox(w);
  625.   w->ms.box_origin.x=w->ms.box_corner.x=((XButtonEvent *) event)->x;
  626.   w->ms.box_origin.y=w->ms.box_corner.y=((XButtonEvent *) event)->y;
  627.   DrawBox(w);
  628. }
  629.  
  630.  
  631. /* Stretch the rubberband box */
  632.  
  633. static void StretchBoxAction(w, event, params, nparams)
  634.      MsWidget w;
  635.      XEvent *event;
  636.      String *params;
  637.      Cardinal *nparams;
  638. { EraseBox(w);
  639.   w->ms.box_corner.x=((XButtonEvent *) event)->x;
  640.   w->ms.box_corner.y=((XButtonEvent *) event)->y;
  641.   DrawBox(w);
  642. }
  643.  
  644.  
  645. /* Stop stretching the rubberband box (currently a no-op) */
  646.  
  647. static void EndBoxAction(w, event, params, nparams)
  648.      MsWidget w;
  649.      XEvent *event;
  650.      String *params;
  651.      Cardinal *nparams;
  652. {
  653. }
  654.  
  655. /* 
  656.   Redraw the parts of the rubberband box that may have been 
  657.   damaged by painting the given rectangle.
  658. */
  659.   
  660. static void RepairBox(w, x, y, width, height)
  661.      MsWidget w;
  662.      int x, y, width, height;
  663. { XRectangle rect;
  664.  
  665.   if(!w->ms.box_exists)
  666.     return;
  667.  
  668.   rect.x = x;
  669.   rect.y = y;
  670.   rect.width = width;
  671.   rect.height = height;
  672.   XSetClipRectangles(XtDisplay(w), w->ms.box_gc,
  673.              0, 0, &rect, 1, YXBanded);
  674.   InvertBox(w);
  675.   XSetClipMask(XtDisplay(w), w->ms.box_gc, None);
  676. }
  677.  
  678.  
  679. /* Realize the widget */
  680.  
  681. static void
  682. Realize (w, valueMask, attrs)
  683.      MsWidget w;
  684.      XtValueMask *valueMask;
  685.      XSetWindowAttributes *attrs;
  686. { attrs->backing_store = Always;
  687.   attrs->save_under = False;
  688.   attrs->bit_gravity = ForgetGravity;
  689.   attrs->cursor = w->ms.my_cursor;
  690.   attrs->colormap = MamaColormap(w->ms.mama);
  691.   XtCreateWindow((Widget) w, InputOutput, MamaVisualInfo(w->ms.mama)->visual,
  692.     *valueMask | CWBackingStore | CWSaveUnder| CWBitGravity | CWCursor |
  693.          CWColormap, attrs);
  694.   MsPrecalculate(w);
  695. #ifdef MENU
  696.   MsCreateMenu(w, !w->ms.xi.julia, MamaVisualInfo(w->ms.mama)->class == PseudoColor);
  697. #endif
  698. }
  699.  
  700.  
  701. /* Destroy the widget */
  702.  
  703. static void
  704. Destroy (w)
  705.      MsWidget w;
  706. { XtReleaseGC((Widget) w, w->ms.box_gc);
  707.   XtFree((char *) w->ms.rectbuffer);
  708. }
  709.  
  710.  
  711. /* Update widget resources */
  712.  
  713. static Boolean SetValues(current, request, new)
  714.      MsWidget current, request, new;
  715. { MsPrecalculate(new); /* do the dirty work */
  716.   return(True); /* widget must be redisplayed */
  717. }
  718.  
  719.  
  720. /* Resize the widget */
  721.  
  722. static void
  723. Resize(w)
  724.      MsWidget w;
  725. { MsPrecalculate(w); /* do the dirty work */
  726. }
  727.  
  728.  
  729. /* Print the view coordinates */
  730.  
  731. void WindowStats(w)
  732.      MsWidget w;
  733. { (void) printf("current picture area: x = %f .. %f, y = %f .. %f\n",
  734.         w->ms.xi.center_x - w->ms.xi.xrange / 2,
  735.         w->ms.xi.center_x + w->ms.xi.xrange / 2,
  736.         w->ms.xi.center_y - w->ms.xi.yrange / 2,
  737.         w->ms.xi.center_y + w->ms.xi.yrange / 2
  738.            );
  739.   (void) printf("return here with: xms ");
  740.   if(w->ms.xi.julia)
  741.     (void) printf("-julia -cx %f -cy %f ", w->ms.xi.c_x, w->ms.xi.c_y);
  742.   (void) printf("-x %f -y %f -range %f\n",
  743.            w->ms.xi.center_x, w->ms.xi.center_y, w->ms.xi.xrange);
  744. }
  745.  
  746.  
  747. /* Action routine for the above */
  748.  
  749. static void WindowStatsAction(w, event, params, nparams)
  750.      MsWidget w;
  751.      XEvent *event;
  752.      String *params;
  753.      Cardinal *nparams;
  754. { WindowStats(w);
  755. }
  756.  
  757.  
  758. /* Print slave performance statistics */
  759.  
  760. static void ApplStatsAction(w, event, params, nparams)
  761.      MsWidget w;
  762.      XEvent *event;
  763.      String *params;
  764.      Cardinal *nparams;
  765. { SlaveStatistics(w->ms.mama);
  766. }
  767.  
  768.  
  769. /* auxiliary macro for ms_draw: */
  770.  
  771. #define DRAW_SINGLEPLANE(iter_type, init_mask, shift_op) \
  772.  { iter_type *datap = (iter_type *) data; \
  773.    for(j=0; j<height; j++) \
  774.    { unsigned long *bufp = (unsigned long *) \
  775.        ((char *) w->ms.rectbuffer->data + \
  776.           (w->ms.rectbuffer->bytes_per_line * j)); \
  777.      for(i=0; i<width; ) /* for each word */ \
  778.      { unsigned long pixword = 0; \
  779.        unsigned long mask = init_mask; \
  780.        unsigned end = MIN(width, i+32); \
  781.        for(; i<end; i++) /* for each bit in the byte */ \
  782.        { if(pixels[*datap++]) \
  783.        pixword |= mask; \
  784.      mask shift_op 1; \
  785.        } \
  786.        *bufp++ = pixword; \
  787.      } \
  788.    } \
  789.  }
  790.  
  791.  
  792. /* This function is called when a chunk has been completed by a slave */
  793. /* to draw it on the screen */
  794.  
  795. void ms_draw(client, client_data, data)
  796.      char *client;
  797.      char *client_data;
  798.      char *data; 
  799. { MsWidget w = (MsWidget) client;
  800.   register int i, j;
  801.   unsigned int x, y, width, height;
  802.  
  803.   ms_client_info *the_info = (ms_client_info *) client_data;
  804.  
  805.   unsigned long *pixels = MamaPixels(w->ms.mama);
  806.  
  807.   /* Ignore the reply if the widget has changed shape or something */
  808.   if(the_info->configuration != w->ms.xi.configuration)
  809.     return;
  810.  
  811.   x=the_info->s.x; 
  812.   y=the_info->s.y; 
  813.   width=the_info->s.width;
  814.   height=the_info->s.height;
  815.  
  816.   /*
  817.     16-bit iteration counts from the slave need to be
  818.     converted from network byte order.  It is faster
  819.     to do it all at once here than to call ntohs()
  820.     in the tight loops below.  
  821.   */
  822.   if(CPU_LITTLE_ENDIAN())
  823.   { if(w->ms.xi.bytes_per_count == sizeof(unsigned short))
  824.     {    unsigned short *end;
  825.       register unsigned char *p;
  826.       end = (unsigned short *) data + (width * height);
  827.       for(p = (unsigned char *) data; p < (unsigned char *) end; p += 2)
  828.       { unsigned char tmp;
  829.     tmp = *p;
  830.     *p = *(p+1);
  831.     *(p+1) = tmp;
  832.       }
  833.     }
  834.   }
  835.  
  836.   /*
  837.     Sorry about the combinatorial explosion, but this part easily 
  838.     becomes a bottleneck if all the tests are done inside the loop
  839.   */
  840.   switch(w->ms.type)
  841.   {
  842.   case ITER_BYTE|DISP_1plane32msb:
  843.     DRAW_SINGLEPLANE(unsigned char, 0x80000000, >>=);
  844.     break;
  845.   case ITER_WORD|DISP_1plane32msb:
  846.     DRAW_SINGLEPLANE(unsigned short, 0x80000000, >>=);
  847.     break;
  848.   case ITER_BYTE|DISP_1plane32lsb:
  849.     DRAW_SINGLEPLANE(unsigned char, 0x00000001, <<=);
  850.     break;
  851.   case ITER_WORD|DISP_1plane32lsb:
  852.     DRAW_SINGLEPLANE(unsigned short, 0x00000001, <<=);
  853.     break;
  854.   case ITER_BYTE|DISP_8plane:
  855.     /* Quick-and-dirty drawing of 8-bit values on 8-plane displays */
  856.     { unsigned char *datap = (unsigned char *) data;
  857.       for(j=0; j<height; j++)
  858.       { unsigned char *bufp= (unsigned char *)
  859.       w->ms.rectbuffer->data + (w->ms.rectbuffer->bytes_per_line * j);
  860.     for(i=0; i<width; i++)
  861.     { *bufp++ = pixels[*datap++];
  862.     }
  863.       }
  864.     }
  865.     break;
  866.   case ITER_WORD|DISP_8plane:
  867.     /* Quick-and-dirty drawing of 16-bit values on 8-plane displays */
  868.     { unsigned short *datap = (unsigned short *) data;
  869.       for(j=0; j<height; j++)
  870.       { unsigned char *bufp= (unsigned char *)
  871.       w->ms.rectbuffer->data + (w->ms.rectbuffer->bytes_per_line * j);
  872.     for(i=0; i<width; i++)
  873.     { *bufp++ = pixels[*datap++];
  874.     }
  875.       }
  876.     }
  877.     break;
  878.   case ITER_BYTE|DISP_32plane:
  879.     /* Quick-and-dirty drawing of 8-bit values on 24/32-plane displays */
  880.     { unsigned char *datap = (unsigned char *) data;
  881.       for(j=0; j<height; j++)
  882.       { unsigned long *bufp= (unsigned long *) ((unsigned char *)
  883.      w->ms.rectbuffer->data + (w->ms.rectbuffer->bytes_per_line * j));
  884.     for(i=0; i<width; i++)
  885.     { *bufp++ = pixels[*datap++];
  886.     }
  887.       }
  888.     }
  889.     break;
  890.   case ITER_WORD|DISP_32plane:
  891.     /* Quick-and-dirty drawing of 16-bit values on 24/32-plane displays */
  892.     { unsigned short *datap = (unsigned short *) data;
  893.       for(j=0; j<height; j++)
  894.       { unsigned long *bufp= (unsigned long *) ((unsigned char *)
  895.      w->ms.rectbuffer->data + (w->ms.rectbuffer->bytes_per_line * j));
  896.     for(i=0; i<width; i++)
  897.     { *bufp++ = pixels[*datap++];
  898.     }
  899.       }
  900.     }
  901.     break;
  902.   case ITER_BYTE|DISP_generic:
  903.     /* Slow but portable drawing of 8-bit values on 8-plane displays */
  904.     { unsigned char *datap = (unsigned char *) data;
  905.       for(j=0; j<height; j++)
  906.       { for(i=0; i<width; i++)
  907.       XPutPixel(w->ms.rectbuffer, i, j, pixels[*datap++]);
  908.       }
  909.     }
  910.     break;
  911.   case ITER_WORD|DISP_generic:
  912.     /* Slow but portable drawing of 16-bit values on 8-plane displays */
  913.     { unsigned short *datap = (unsigned short *) data;
  914.       for(j=0; j<height; j++)
  915.       { for(i=0; i<width; i++)
  916.       XPutPixel(w->ms.rectbuffer, i, j, pixels[*datap++]);
  917.       }
  918.     }
  919.     break;
  920.   default:
  921.     XtAppError(thisApp, "no drawing routine defined for this display type");
  922.   }
  923.  
  924.   if(w->ms.sony_bug_workaround)
  925.   { /* ugly workaround for a bug causing some Sony X servers to */
  926.     /* crash in greyscale mode */
  927.     height=32;
  928.     width=32;
  929.   }
  930.  
  931.   /* go do it! */
  932.   XPutImage(XtDisplay(w), XtWindow(w), w->ms.blit_gc, 
  933.         w->ms.rectbuffer, 
  934.         0, 0, /* position in the buffer */
  935.         x, y, /* position on the screen */
  936.         width, height);
  937.  
  938.   RepairBox(w, x, y, width, height);
  939. }
  940.  
  941.  
  942. /* Destroy this window (only) */
  943.  
  944. static void
  945. CloseAction(w, event, params, nparams)
  946.      MsWidget w;
  947.      XEvent *event;
  948.      String *params;
  949.      Cardinal *nparams;
  950. { XtDestroyWidget(XtParent(w)); /* destroy the shell widget */
  951. }
  952.  
  953.  
  954. /* Ask Mama to shut down */
  955.  
  956. static void 
  957. QuitAction(w, event, params, nparams)
  958.      MsWidget w;
  959.      XEvent *event;
  960.      String *params;
  961.      Cardinal *nparams;
  962. { Shutdown(w->ms.mama);
  963. }
  964.  
  965.  
  966. /* Widget destruction callback */
  967.  
  968. static void
  969. Die(w, client_data, call_data)
  970.      MsWidget w;
  971.      caddr_t client_data;
  972.      caddr_t call_data;
  973. {
  974.   /* inform the slave handler that we don't want any more replies */
  975.   wf_client_died(MamaWorkforce(w->ms.mama), (char *) &w->ms.xi);
  976. }
  977.